package com.atguigu.tingshu.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.hash.Hash;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.cache.GuiGuCache;
import com.atguigu.tingshu.common.constant.KafkaConstant;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.service.KafkaService;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.user.*;
import com.atguigu.tingshu.user.mapper.*;
import com.atguigu.tingshu.user.pattern.UserPaidStrategy;
import com.atguigu.tingshu.user.pattern.fac.StrategyFactory;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private WxMaService wxMaService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private KafkaService kafkaService;


    /**
     * 微信登录
     *
     * @param code 小程序提交来临时票据，用于调用微信获取微信账户基本接口
     * @return {token:"令牌"}
     */
    @Override
    public Map<String, String> wxLogin(String code) {
        try {
            //1.调用微信接口获取微信账户基本信息(微信账户唯一标识)
            WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
            if (sessionInfo != null) {
                String wxOpenid = sessionInfo.getOpenid();
                //2.根据微信账户唯一标识wxOpenID查询用户记录 判断是否首次登录APP
                LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(UserInfo::getWxOpenId, wxOpenid);
                UserInfo userInfo = userInfoMapper.selectOne(queryWrapper);
                //3.如果是首次登录，新增用户及初始化账户
                if (userInfo == null) {
                    //3.1 构建用户信息对象
                    userInfo = new UserInfo();
                    userInfo.setNickname("听友" + IdUtil.getSnowflakeNextId());
                    userInfo.setAvatarUrl("https://thirdwx.qlogo.cn/mmopen/vi_32/hFKeRpRQU4wG…axvke5nueicggowdBricR4pspWbp6dwFtLSCWJKyZGJoQ/132");
                    userInfo.setWxOpenId(wxOpenid);
                    userInfo.setIsVip(0);
                    //3.2 保存用户记录（自定义用户跟微信用户绑定）
                    userInfoMapper.insert(userInfo);
                    //3.3 发送Kafka消息完成异步初始化账户（生产者）
                    kafkaService.sendMessage(KafkaConstant.QUEUE_USER_REGISTER, userInfo.getId().toString());
                }
                //4.生成令牌，将令牌存入Redis，响应令牌给到客户端
                //4.1 产生UUID令牌
                String token = IdUtil.randomUUID();
                //4.2 生成登录key
                String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
                //4.3 存入Redis 值为用户基本信息 UserInfoVo
                UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class);
                redisTemplate.opsForValue().set(loginKey, userInfoVo, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);
                //4.4 响应token给到客户端
                Map<String, String> map = new HashMap<>();
                map.put("token", token);
                return map;
            }
            return null;
        } catch (Exception e) {
            log.error("[用户服务]登录异常：", e);
            throw new GuiguException(500, "登录失败:" + e.getMessage());
        }
    }

    /**
     * 根据用户ID查询用户基本信息
     *
     * @param userId
     * @return
     */
    @Override
    @GuiGuCache(prefix = "user:info:")
    public UserInfoVo getUserInfo(Long userId) {
        //1.根据ID查询用户PO对象
        UserInfo userInfo = userInfoMapper.selectById(userId);

        //2.将用户PO对象转为VO
        if (userInfo != null) {
            return BeanUtil.copyProperties(userInfo, UserInfoVo.class);
        }
        return null;
    }

    /**
     * 修改当前用户基本信息
     *
     * @param userInfoVo
     * @return
     */
    @Override
    public void updateUser(Long userId, UserInfoVo userInfoVo) {
        //1.先删除缓存
        //2.执行修改
        UserInfo userInfo = new UserInfo();
        userInfo.setId(userId);
        userInfo.setNickname(userInfoVo.getNickname());
        userInfo.setAvatarUrl(userInfoVo.getAvatarUrl());
        userInfoMapper.updateById(userInfo);
        //3.睡眠一段时间
        //4.再删除缓存
    }

    @Autowired
    private UserPaidAlbumMapper userPaidAlbumMapper;

    @Autowired
    private UserPaidTrackMapper userPaidTrackMapper;

    /**
     * 专辑详情中每页声音列表(非免费试听)，对提交专辑以及声音ID进行判断得出每个声音购买情况
     *
     * @param userId                       用户ID
     * @param albumId                      专辑ID
     * @param needCheckBuyStausTrackIdList 需要判断购买状态声音ID集合
     * @return {20158:1,20159:1}
     */
    @Override
    public Map<Long, Integer> getCheckBuyStausTrackIdList(Long userId, Long albumId, List<Long> needCheckBuyStausTrackIdList) {
        Map<Long, Integer> map = new HashMap<>();
        //1.判断用户是否已购买专辑
        //1.1 根据用户ID+专辑ID查询专辑购买情况
        LambdaQueryWrapper<UserPaidAlbum> userPaidAlbumLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userPaidAlbumLambdaQueryWrapper.eq(UserPaidAlbum::getUserId, userId);
        userPaidAlbumLambdaQueryWrapper.eq(UserPaidAlbum::getAlbumId, albumId);
        userPaidAlbumLambdaQueryWrapper.last("limit 1");
        Long count = userPaidAlbumMapper.selectCount(userPaidAlbumLambdaQueryWrapper);
        //1.2 如果购买专辑，将提交所有声音购买情况设置为：1 返回
        if (count > 0) {
            for (Long trackId : needCheckBuyStausTrackIdList) {
                map.put(trackId, 1);
            }
            return map;
        }

        //2.判断用户购买声音情况
        //2.1 根据用户ID+专辑ID查询已购声音列表
        LambdaQueryWrapper<UserPaidTrack> userPaidTrackLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userPaidTrackLambdaQueryWrapper.eq(UserPaidTrack::getUserId, userId);
        userPaidTrackLambdaQueryWrapper.eq(UserPaidTrack::getAlbumId, albumId);
        userPaidTrackLambdaQueryWrapper.select(UserPaidTrack::getTrackId);
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(userPaidTrackLambdaQueryWrapper);
        //2.2 如果不存在已购声音，将提交所有声音购买情况设置为：0 返回
        if (CollectionUtil.isEmpty(userPaidTrackList)) {
            for (Long trackId : needCheckBuyStausTrackIdList) {
                map.put(trackId, 0);
            }
            return map;
        }
        //2.3 如果存在已购声音，动态判断声音购买情况(遍历待检查声音购买情况ID列表，判断ID是否包含在已购声音ID列表中)
        List<Long> userPaidTrackIdList = userPaidTrackList.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
        for (Long trackId : needCheckBuyStausTrackIdList) {
            if (userPaidTrackIdList.contains(trackId)) {
                map.put(trackId, 1);
            } else {
                map.put(trackId, 0);
            }
        }
        return map;
    }

    /**
     * 判断指定用户ID是否购买过指定专辑
     *
     * @param userId
     * @param albumId
     * @return
     */
    @Override
    public Boolean isPaidAlbum(Long userId, Long albumId) {
        //1.构建查询条件：用户ID+专辑ID
        LambdaQueryWrapper<UserPaidAlbum> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserPaidAlbum::getUserId, userId);
        queryWrapper.eq(UserPaidAlbum::getAlbumId, albumId);
        Long count = userPaidAlbumMapper.selectCount(queryWrapper);
        //2.查询已购专辑数量判断
        return count > 0;
    }

    /**
     * 查询指定用户某个专辑已购声音ID列表
     *
     * @param albumId
     * @return
     */
    @Override
    public List<Long> getUserPaidTrackIdList(Long userId, Long albumId) {
        //1.构建查询条件 用户ID+专辑ID查询声音列 故新建复合索引：包含用户ID，专辑ID，声音ID
        LambdaQueryWrapper<UserPaidTrack> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserPaidTrack::getUserId, userId);
        queryWrapper.eq(UserPaidTrack::getAlbumId, albumId);
        queryWrapper.select(UserPaidTrack::getTrackId);
        //2.得到已购声音列表
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(queryWrapper);
        if (CollectionUtil.isNotEmpty(userPaidTrackList)) {
            //3.获取已购声音ID列表
            List<Long> userPaidTrackIdList = userPaidTrackList.stream()
                    .map(UserPaidTrack::getTrackId)
                    .collect(Collectors.toList());
            return userPaidTrackIdList;
        }
        return null;
    }

    @Autowired
    private StrategyFactory strategyFactory;


    /**
     * 支付（余额，微信）成功后虚拟物品发货
     * 可能会存在三种项目类型：1001-专辑 1002-声音 1003-vip会员
     *
     * @param userPaidRecordVo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
        String itemType = userPaidRecordVo.getItemType();
        //1.根据购买项目类型获取该购买项目策略实现类对象
        UserPaidStrategy paidStrategy = strategyFactory.getPaidStrategy(itemType);
        //2.处理不同购买项目类型
        paidStrategy.handlerPaidRecord(userPaidRecordVo);
    }

    /**
     * 更新VIP状态：处理过期会员
     *
     * @return
     */
    @Override //2024-04-25 00:00:01
    public void updateVipExpireStatus(Date date) {
        LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserInfo::getIsVip, 1);
        queryWrapper.lt(UserInfo::getVipExpireTime, date);
        queryWrapper.select(UserInfo::getId);
        List<UserInfo> vipUserList = userInfoMapper.selectList(queryWrapper);
        if (CollectionUtil.isNotEmpty(vipUserList)) {
            //修改用户会员标识
            vipUserList.stream().forEach(userInfo -> {
                userInfo.setIsVip(0);
            });
            //批量更新
            this.saveOrUpdateBatch(vipUserList);
        }
    }
}
